/*
 * File: Yahtzee.java
 * ------------------
 * Implementation of the Yahtzee game
 */

import acm.io.*;
import acm.program.*;
import acm.util.*;

public class Yahtzee extends GraphicsProgram implements YahtzeeConstants {
	public void run() {
		setupPlayers();
		initDisplay();
		playGame();
	}
	
	/**
	 * Prompts the user for information about the number of players, then sets up the
	 * players array and number of players.
	 */
	private void setupPlayers() {
		nPlayers = chooseNumberOfPlayers();	
		
		/* Set up the players array by reading names for each player. */
		playerNames = new String[nPlayers];
		for (int i = 0; i < nPlayers; i++) {
			/* IODialog is a class that allows us to prompt the user for information as a
			 * series of dialog boxes.  We will use this here to read player names.
			 */
			IODialog dialog = getDialog();
			playerNames[i] = dialog.readLine("Enter name for player " + (i + 1));
		}
	}
	
	/**
	 * Prompts the user for a number of players in this game, reprompting until the user
	 * enters a valid number.
	 * 
	 * @return The number of players in this game.
	 */
	private int chooseNumberOfPlayers() {
		/* See setupPlayers() for more details on how IODialog works. */
		IODialog dialog = getDialog();
		
		while (true) {
			/* Prompt the user for a number of players. */
			int result = dialog.readInt("Enter number of players");
			
			/* If the result is valid, return it. */
			if (result > 0 && result <= MAX_PLAYERS)
				return result;
			
			dialog.println("Please enter a valid number of players.");
		}
	}
	
	/**
	 * Sets up the YahtzeeDisplay associated with this game.
	 */
	private void initDisplay() {
		display = new YahtzeeDisplay(getGCanvas(), playerNames);
	}

	/**
	 * Actually plays a game of Yahtzee.  This is where you should begin writing your
	 * implementation.
	 */
	private void playGame() {
		initializeScores();
		
		for (int currentCategory=0; currentCategory<N_SCORING_CATEGORIES; currentCategory++){
			for (int currentPlayer=0; currentPlayer<nPlayers; currentPlayer++){
				doFirstRoll(currentPlayer);
	
				doSecondRoll();
				
				doThirdRoll();
				
				chooseCategory(currentPlayer);
			}
		}
		
		calculateFinalResults();
		
		andTheWinnerIs();
	}
	
	private void initializeScores(){
		scores = new int[nPlayers][N_CATEGORIES];
		for(int i=0; i<nPlayers; i++){
			for(int j=0; j<N_CATEGORIES; j++){
				scores[i][j] = -1;
			}
		}
	}
	
	/**
	 *  Concentrates the logic for N rolls after the first one
	 */
	private void doNextRoll(){
		display.waitForPlayerToSelectDice();
		for (int i=0; i<N_DICE; i++){
			if (display.isDieSelected(i)){
				diceRoll[i] = rgen.nextInt(1,6);
			}
		}
		display.displayDice(diceRoll);
	}
	
	/**
	 * Lets the player select the category where to store results
	 * @param currentPlayer the actual player of the turn
	 */
	private void chooseCategory(int currentPlayer){
		display.printMessage("Select a category for this roll");
		while(true)
		{
			int category=display.waitForPlayerToSelectCategory();
			
			if (!categorySelected(currentPlayer, category)){
				displayScore(currentPlayer, category);
				break;
			}
			
			display.printMessage("You have already selected this category. Please select another one.");
		}
	}
	
	/**
	 * Calculates upper, lower, and total results
	 */
	private void calculateFinalResults(){
		for (int currentPlayer = 0; currentPlayer < nPlayers; currentPlayer++){
			display.updateScorecard(LOWER_SCORE, currentPlayer, scores[currentPlayer][LOWER_SCORE]);
			display.updateScorecard(UPPER_SCORE, currentPlayer, scores[currentPlayer][UPPER_SCORE]);
			display.updateScorecard(UPPER_BONUS, currentPlayer, calculateUpperBonus(currentPlayer));
			display.updateScorecard(TOTAL, currentPlayer, scores[currentPlayer][TOTAL]);
		}
	}
	
	/**
	 * Determines which of the players is the winner of the game
	 */
	private void andTheWinnerIs(){
		
	}
	
	/**
	 * Displays the score of the turn and total
	 * @param currentPlayer
	 * @param category
	 */
	private void displayScore(int currentPlayer, int category){
		if (selectedCategoryCorrespondsToDieResults(category)){
			int score = calculateScore(currentPlayer, category);
			display.updateScorecard(category, currentPlayer, score);
			display.updateScorecard(TOTAL, currentPlayer, getTotalScore(currentPlayer));
		}
		else{
			scores[currentPlayer][category] = 0;
			display.updateScorecard(category, currentPlayer,0);
			display.updateScorecard(TOTAL, currentPlayer, scores[currentPlayer][TOTAL]);
		}
	}
	
	/**
	 * Calculates total score
	 * @param currentPlayer the actual player of the turn
	 * @return the current total score for current player 
	 */
	private int getTotalScore(int currentPlayer){
		checker.calculateTotalScore(scores, currentPlayer);
		
		scores[currentPlayer][TOTAL] = checker.getTotalScore();
		scores[currentPlayer][UPPER_SCORE] = checker.getUpperScore();
		scores[currentPlayer][LOWER_SCORE] = checker.getLowerScore();
		
		return checker.getTotalScore();
	}

	private int calculateUpperBonus(int currentPlayer){
		checker.calculateUpperBonus();
		
		scores[currentPlayer][UPPER_BONUS] = checker.getUpperBonus();
		scores[currentPlayer][TOTAL] = scores[currentPlayer][TOTAL] + scores[currentPlayer][UPPER_BONUS];
		
		return scores[currentPlayer][UPPER_BONUS];
	}
	private int calculateScore(int currentPlayer, int category){
		int score = checker.getScore();
		
		scores[currentPlayer][category] = score;
			
		return score;
	}
	
	private boolean selectedCategoryCorrespondsToDieResults(int category){
		checker = new YahtzeeBrain(diceRoll, category);

		if (	checker.isSimple()
			||	checker.isFullHouse() 
			|| 	checker.isThreeOfAKind()
			|| 	checker.isFourOfAKind()
			|| 	checker.isSmallStraight()
			|| 	checker.isLargeStraight()
			|| 	checker.isYahtzee()){
			return true;
		}
		
		return false;
		
	}
	
	private int getScoreForPlayerAndCategory(int player, int category){
		return scores[player][category];
	}
	
	private boolean categorySelected (int currentPlayer, int category){
		return getScoreForPlayerAndCategory(currentPlayer, category) != -1;
	}
	
	/////////////////////////////////////////
	//           Dice Rolling
	/////////////////////////////////////////
	
	/**
	 *  Rolls the dice for the first time 
	 */
	private void doFirstRoll(int currentPlayer){
		display.printMessage(playerNames[currentPlayer] + "'s turn! Click the " + "\"Roll Dice\" " + "button to roll the dice.");
		
		rollDice(); 
		display.waitForPlayerToClickRoll(currentPlayer);
		display.displayDice(diceRoll);
	}
	
	/**
	 *  Rolls the dice for the second time 
	 */
	private void doSecondRoll(){
		display.printMessage("Select the dice you wish to re-roll and click " + "\"Roll Again\"");
		doNextRoll();
	}
	
	/**
	 *  Rolls the dice for third first time 
	 */
	private void doThirdRoll(){
		display.printMessage("Select the dice you wish to re-roll and click " + "\"Roll Again\"");
		doNextRoll();
	}
	
	/**
	 * Rolls the dice
	 */
	private void rollDice(){
		for (int die=0; die<N_DICE; die++){
			diceRoll[die] = rgen.nextInt(1,6); 
		}
	}
	
	/* Private instance variables */
	private int nPlayers;
	private String[] playerNames;
	private YahtzeeDisplay display;
	private RandomGenerator rgen = RandomGenerator.getInstance();
	private int[] diceRoll = new int[N_DICE]; 
	private int[][] scores;
	YahtzeeBrain checker;
} 
